package br.com.winesys.geta.crud;

import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import net.miginfocom.swing.MigLayout;
import br.com.winesys.geta.base.ExceptionMessage;
import br.com.winesys.geta.databinding.component.listeners.SelectionListener;
import br.com.winesys.geta.databinding.component.selector.ObjectSelector;
import br.com.winesys.geta.dialog.BaseDialog;
import br.com.winesys.geta.editor.Editor;

public abstract class CadastroUnico<Entity> extends BaseDialog implements SelectionListener<Entity> {

    protected Action newAction;
    protected Action editAction;
    protected Action deleteAction;
    protected Action saveAction;
    protected Action cancelAction;
    protected boolean inserting = false;
    protected boolean editing = false;
    protected Entity selected;
    private ObjectSelector<Entity> selector;
    private Editor<Entity> editor;

    public CadastroUnico(Window owner, String title) {
        super(owner, title, null);
    }

    @Override
    protected JComponent createComponent() {
        this.selector = createObjectSelector();
        this.selector.addSelectionListener(this);
        JPanel panel = new JPanel();
        panel.setLayout(new MigLayout());
        JPanel northPanel = new JPanel();
        northPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
        northPanel.add(new JLabel("Selecione um item:"));
        northPanel.add(this.selector);
        panel.add(northPanel, "cell 0 0, growx");
        this.editor = createObjectEditor();
        this.editor.setEnabled(false);
        panel.add(this.editor.getComponent(), "cell 0 1, growx");
        panel.doLayout();
        return panel;
    }

    @Override
    protected void createActions() {
        this.newAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                CadastroUnico.this.doNew();
            }
        };
        this.newAction.setEnabled(canInsert());
        this.newAction.putValue(Action.NAME, "Novo");
        this.newAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_N);
        this.newAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke('N', InputEvent.CTRL_MASK));
        this.newAction.putValue(Action.LARGE_ICON_KEY, new ImageIcon(CadastroUnico.class.getResource("/images/new.png")));

        this.editAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                CadastroUnico.this.doEdit();
            }
        };
        this.editAction.setEnabled(false);
        this.editAction.putValue(Action.NAME, "Editar");
        this.editAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_E);
        this.editAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke('E', InputEvent.CTRL_MASK));
        this.editAction.putValue(Action.LARGE_ICON_KEY, new ImageIcon(CadastroUnico.class.getResource("/images/edit.png")));

        this.deleteAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                CadastroUnico.this.doDelete();
            }
        };
        this.deleteAction.setEnabled(false);
        this.deleteAction.putValue(Action.NAME, "Excluir");
        this.deleteAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X);
        this.deleteAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
        this.deleteAction.putValue(Action.LARGE_ICON_KEY, new ImageIcon(CadastroUnico.class.getResource("/images/delete.png")));

        this.saveAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                CadastroUnico.this.doSave();
            }
        };
        this.saveAction.setEnabled(false);
        this.saveAction.putValue(Action.NAME, "Salvar");
        this.saveAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
        this.saveAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        this.saveAction.putValue(Action.LARGE_ICON_KEY, new ImageIcon(CadastroUnico.class.getResource("/images/save.png")));

        this.cancelAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent evt) {
                CadastroUnico.this.doCancel();
            }
        };
        this.cancelAction.setEnabled(false);
        this.cancelAction.putValue(Action.NAME, "Cancelar");
        this.cancelAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_C);
        this.cancelAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.CTRL_MASK));
        this.cancelAction.putValue(Action.LARGE_ICON_KEY, new ImageIcon(CadastroUnico.class.getResource("/images/cancel.png")));
    }

    @Override
    protected JMenuBar createMenuBar() {
        return null;
    }

    @Override
    protected JToolBar createToolBar() {
        JToolBar toolBar = new JToolBar(SwingConstants.HORIZONTAL);
        toolBar.setFloatable(false);
        toolBar.add(this.newAction);
        toolBar.add(this.editAction);
        toolBar.add(this.deleteAction);
        toolBar.addSeparator();
        toolBar.add(this.saveAction);
        toolBar.add(this.cancelAction);
        toolBar.addSeparator();
        return toolBar;
    }

    @Override
    protected JPanel createStatus() {
        return null;
    }

    @Override
    public void itemSelected(Entity obj) {
        if (!this.inserting && !this.editing) {
            this.selected = obj;
            this.editor.setObject(obj);
            this.refreshActions();
        }
    }

    private void refreshActions() {
        this.selector.setEnabled(!this.inserting && !this.editing);
        this.newAction.setEnabled(!this.inserting && !this.editing && canInsert());
        this.editAction.setEnabled(!this.inserting && !this.editing && this.selected != null && canEdit());
        this.deleteAction.setEnabled(!this.inserting && !this.editing && this.selected != null && canDelete());
        this.saveAction.setEnabled(this.inserting || this.editing);
        this.cancelAction.setEnabled(this.inserting || this.editing);
    }

    protected boolean canInsert() {
        return true;
    }

    protected boolean canEdit() {
        return true;
    }

    protected boolean canDelete() {
        return true;
    }

    private void doNew() {
        this.selected = this.createNewObject();
        this.inserting = true;
        this.editor.setObject(this.selected);
        this.editor.setEnabled(true);
        this.selector.setSelected(null);
        this.refreshActions();
        this.editor.getComponent().requestFocus();
    }

    private void doEdit() {
        this.editing = true;
        this.editor.setEnabled(true);
        this.refreshActions();
        this.editor.getComponent().requestFocus();
    }

    private void doDelete() {
        if (JOptionPane.showConfirmDialog(this.dialog, "Tem certeza que deseja excluir o item?", "Excluir", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
            try {
                this.delete(this.selected);
                this.selector.setSelected(null);
            } catch (Exception ex) {
                Toolkit.getDefaultToolkit().beep();
                JOptionPane.showMessageDialog(this.dialog, ExceptionMessage.getMessage("Erro ao excluir o item selecionado.", ex), "Erro", JOptionPane.ERROR_MESSAGE);
            }
            this.itemSelected(null);
        }
    }

    private void doSave() {
        if (this.editor.validate()) {
            this.selected = this.editor.getObject();
            if (this.editing) {
                try {
                    this.update(this.selected);
                } catch (Exception ex) {
                    Toolkit.getDefaultToolkit().beep();
                    JOptionPane.showMessageDialog(this.dialog, ExceptionMessage.getMessage("Erro ao executar a alteração.", ex), "Erro", JOptionPane.ERROR_MESSAGE);
                }
            } else {
                try {
                    this.insert(this.selected);
                } catch (Exception ex) {
                    Toolkit.getDefaultToolkit().beep();
                    JOptionPane.showMessageDialog(this.dialog, ExceptionMessage.getMessage("Erro ao executar a inclusão.", ex), "Erro", JOptionPane.ERROR_MESSAGE);
                }
            }
            this.inserting = false;
            this.editing = false;
            this.selector.setSelected(this.selected);
            this.editor.setEnabled(false);
            this.refreshActions();
            this.selector.requestFocus();
            this.dialog.pack();
        }
    }

    private void doCancel() {
        if (this.editing) {
            this.itemSelected(this.selected);
        } else {
            this.itemSelected(null);
        }
        this.inserting = false;
        this.editing = false;
        this.editor.cleanValidation();
        this.editor.setEnabled(false);
        this.editor.setObject(this.selected);
        this.refreshActions();
        this.selector.requestFocus();
        this.dialog.pack();
    }

    protected abstract void delete(Entity obj);

    protected abstract void insert(Entity obj);

    protected abstract void update(Entity obj);

    protected abstract Entity createNewObject();

    protected abstract ObjectSelector<Entity> createObjectSelector();

    protected abstract Editor<Entity> createObjectEditor();
}
